﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ExportStep.cs" company="">
//   
// </copyright>
// --------------------------------------------------------------------------------------------------------------------
namespace ExecutionTool.ImportExport
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Threading.Tasks;

    using ExecutionTool.Common;
    using ExecutionTool.CopyDatabase;
    using ExecutionTool.Profile;

    using Microsoft.Practices.EnterpriseLibrary.Logging;

    public class ExportStep : StepBase<ExportStepInfo>
    {
        #region Fields

        private readonly CopyDatabaseStepInfo copyDatabaseStepInfo;

        private readonly IImportExportServiceAdatper importExportServiceAdatper;

        private readonly bool isFromDatabaseCopy;

        private readonly ISqlAzureAdapter sqlAzureAdapter;

        private readonly IStorageAdapter storageAdapter;

        private string blobUri;

        private DataCenter dataCenter;

        private ExportStepInfo exportStepInfo;

        #endregion

        #region Constructors and Destructors

        public ExportStep(
            Profile profile, 
            IImportExportServiceAdatper importExportServiceAdatper, 
            IStorageAdapter storageAdapter, 
            ISqlAzureAdapter sqlAzureAdapter, 
            LogWriter logger, 
            CopyDatabaseStepInfo copyDatabaseStepInfo = null)
            : base(profile, true, logger)
        {
            Helper.CheckForNull(importExportServiceAdatper, "importExportServiceAdatper");
            Helper.CheckForNull(storageAdapter, "storageAdapter");
            Helper.CheckForNull(sqlAzureAdapter, "sqlAzureAdapter");

            this.importExportServiceAdatper = importExportServiceAdatper;
            this.storageAdapter = storageAdapter;
            this.sqlAzureAdapter = sqlAzureAdapter;

            this.copyDatabaseStepInfo = copyDatabaseStepInfo;
            this.isFromDatabaseCopy = this.CanAbort = copyDatabaseStepInfo != null;
        }

        #endregion

        #region Methods

        protected override void AbortAction()
        {
            // this will only called when CanAbort (copyDatabaseStepInfo != null) is true which means exporting from a database copy
            // drop the database copy and stop the export operation (this might take a while as seeing in the experiment)
            this.sqlAzureAdapter.DeleteDatabase(this.Profile.Source, this.copyDatabaseStepInfo.TargetDatabaseName);
        }

        protected override void CleanupAction()
        {
            // for this step, Cleanup happens to be the same action as Abort
            if (this.isFromDatabaseCopy)
            {
                this.AbortAction();
            }
        }

        protected override void OnFinished()
        {
            /*
             * fetch the final bacpac size
             * 
             * write metadata: edition, max size & db name
             * 
             * drop database copy if needed
             */
            var size = this.storageAdapter.GetFinalBacpacSizeInBytes(this.Profile.TargetAzureStorage, this.exportStepInfo.BlobAddress);
            this.exportStepInfo.BacpacFileSizeInBytes = size;

            var metadata = new Dictionary<string, string>
                {
                    { "DatabaseName", this.Profile.Source.DatabaseName }, 
                    { "Edition", this.exportStepInfo.DatabaseInfo.Edition.ToString() }, 
                    { "MaxSizeInGb", this.exportStepInfo.DatabaseInfo.MaxSizeInGb.ToString(CultureInfo.InvariantCulture) }
                };
            this.storageAdapter.SetMetadata(this.Profile.TargetAzureStorage, this.exportStepInfo.BlobAddress, metadata);

            if (this.isFromDatabaseCopy)
            {
                // this happens to be the same action as Abort
                this.AbortAction();
            }

            base.OnFinished();
        }

        protected override double ProgressHeartbeatAction()
        {
            var newProgress = this.importExportServiceAdatper.GetOperationProgress(
                this.dataCenter.Endpoint, this.Profile.Source, this.exportStepInfo.StatusTrackId, StepType.Export);
            return newProgress;
        }

        protected override ExportStepInfo StartAction()
        {
            /*
             * - edition, capacity (max size), actual size
             *  - detect against source database if not from a database copy
             * - trigger export operation
             *  - build blob uri
             *    - container: make sure it exists
             *    - blob: make sure it doesn't exist
             *  - detect data center endpoint
             *  - make web request to trigger the operation
             */
            var tasks = new List<Task>();

            SqlAzureDatabaseInfo info = null;
            if (!this.isFromDatabaseCopy)
            {
                var databaseInfoTask =
                    Task<SqlAzureDatabaseInfo>.Factory.StartNew(() => this.sqlAzureAdapter.GetDatabaseInfo(this.Profile.Source)).ContinueWith(
                        t => info = t.Result);
                tasks.Add(databaseInfoTask);
            }
            else
            {
                info = this.copyDatabaseStepInfo.DatabaseInfo;
            }

            var containerName = this.Profile.Name.ToLower();
            var containerNameTask = this.storageAdapter.CreateContainerIfNotExistAsync(this.Profile.TargetAzureStorage, containerName);
            tasks.Add(containerNameTask);

            var dataCenterTask = DataCenterMapper.GetDatacenterFromServerNameAsync(this.Profile.Source.ServerName);
            tasks.Add(dataCenterTask);

            try
            {
                Task.WaitAll(tasks.ToArray());
            }
            catch (Exception ex)
            {
                throw new StepFaultException(
                    "Pre-requisite work failed during export step.", StepType.Export, ErrorCodes.ImportExportExportPrerequisiteError, ex);
            }

            this.dataCenter = dataCenterTask.Result;
            var blobAddress = BuildBlobAddress(containerName, this.Profile.Source.DatabaseName);
            this.blobUri = BuildBlobUri(this.Profile.TargetAzureStorage, blobAddress);

            var requestsId = this.importExportServiceAdatper.TriggerExport(
                this.dataCenter.Endpoint, 
                this.Profile.Source, 
                this.Profile.TargetAzureStorage, 
                this.blobUri, 
                this.isFromDatabaseCopy ? this.copyDatabaseStepInfo.TargetDatabaseName : this.Profile.Source.DatabaseName);
            this.exportStepInfo = new ExportStepInfo(requestsId, blobAddress, 0, info);
            return this.exportStepInfo;
        }

        private static string BuildBlobAddress(string containerName, string databaseName)
        {
            var name = string.Format("{0}/{1}.{2}.bacpac", containerName, databaseName, DateTime.UtcNow.ToString("yyyy-MM-dd-HH-mm-ss"));
            return name;
        }

        private static string BuildBlobUri(AzureStorageLocation targetAzureStorage, string blobAddress)
        {
            return string.Format("https://{0}.blob.core.windows.net/{1}", targetAzureStorage.StorageAccount, blobAddress);
        }

        #endregion
    }
}
